package com.yesallchina.wxapp.officialaccount.api;

import com.fasterxml.jackson.databind.JsonNode;
import com.yesallchina.wxapp.officialaccount.api.beans.JsApiTicket;
import com.yesallchina.wxapp.officialaccount.api.beans.ParamName;
import com.yesallchina.wxapp.officialaccount.ext.ApiDataCache;
import com.yesallchina.wxapp.officialaccount.service.WxOfficialAccountInitializer;
import com.yesallchina.wxapp.officialaccount.utils.JacksonUtils;
import com.yesallchina.wxapp.officialaccount.utils.SHA1Encrypt;
import com.yesallchina.wxapp.officialaccount.utils.WxHttpUtils;
import com.yesallchina.wxapp.officialaccount.utils.WxUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 网页授权
 */
public class WebPageBaseSupportApi implements InitializingBean {

    private final static Logger log = LoggerFactory.getLogger(WebPageBaseSupportApi.class);

    // 网页授权普通票据
    private JsApiTicket jsapiticket;
    // 网页授权卡券票据
    private JsApiTicket cardapiticket;

    private WxOfficialAccountInitializer wxInit;

    private BasicSupportApi basicSupportApi;

    private UserManageApi userManage;

    private Lock jsapilock = new ReentrantLock();

    private Lock cardapilock = new ReentrantLock();

    public WebPageBaseSupportApi(BasicSupportApi basicSupportApi, UserManageApi userManage) {
        this.basicSupportApi = basicSupportApi;
        this.wxInit = basicSupportApi.getWxInit();
        this.userManage = userManage;
    }

    /**
     * 获取卡券相关的api_ticket
     *
     * @return
     */
    public String getCardapiTicket() {
        return getCardapiTicket(0);
    }

    private String getCardapiTicket(int retry) {
        if (retry > 2) {
            log.error("[微信公众号网页授权Ticket(卡券)]: 调用获取失败");
            return null;
        }
        String ticket = getCardapiTicket0();
        if (StringUtils.hasText(ticket)) {
            return ticket;
        }
        try {
            cardapilock.lock();
            ticket = getCardapiTicket0();
            if (StringUtils.hasText(ticket)) {
                return ticket;
            }
            resetApiTicket("wx_card");
            retry++;
            return getCardapiTicket(retry);
        } finally {
            cardapilock.unlock();
        }

    }

    private String getCardapiTicket0() {
        // 优先从自定义的缓存器中获取
        String ticketCacheClass = wxInit.getSpiProp().getCardapiTicketCacheName();
        ApiDataCache<JsApiTicket> ticketCache = null;
        JsApiTicket ticket = this.cardapiticket;
        if (StringUtils.hasText(ticketCacheClass)) {
            ticketCache = wxInit.getAppCtx().getBean(ticketCacheClass);
        }
        if (ticketCache != null) {
            ticket = ticketCache.get();
        }
        if (ticket != null && !ticket.isDateline()) {
            return ticket.getTicket();
        }
        return null;
    }


    /**
     * 获取普通网页授权的js_api_ticket
     *
     * @return
     */
    public String getJsapiTicket() {
        return getJsapiTicket(0);
    }

    private String getJsapiTicket(int retry) {
        if (retry > 2) {
            log.error("[微信公众号网页授权Ticket]: 调用获取失败");
            return null;
        }
        String ticket = getJsapiTicket0();
        if (StringUtils.hasText(ticket)) {
            return ticket;
        }
        try {
            jsapilock.lock();
            ticket = getJsapiTicket0();
            if (StringUtils.hasText(ticket)) {
                return ticket;
            }
            resetApiTicket("jsapi");
            retry++;
            return getJsapiTicket(retry);
        } finally {
            jsapilock.unlock();
        }
    }

    private String getJsapiTicket0() {
        // 优先从自定义的缓存器中获取
        String ticketCacheClass = wxInit.getSpiProp().getJsapiTicketCacheName();
        ApiDataCache<JsApiTicket> ticketCache = null;
        JsApiTicket ticket = this.jsapiticket;
        if (StringUtils.hasText(ticketCacheClass)) {
            ticketCache = wxInit.getAppCtx().getBean(ticketCacheClass);
        }
        if (ticketCache != null) {
            ticket = ticketCache.get();
        }
        if (ticket != null && !ticket.isDateline()) {
            return ticket.getTicket();
        }
        return null;
    }

    private void resetApiTicket(String type) {
        if (log.isDebugEnabled()) {
            log.debug("[微信公众号网页授权Ticket]: 初始化...");
        }
        String url = wxInit.getApiProp().getJsTicketGet();
        Map<String, String> params = new HashMap<>();
        params.put(ParamName.ACCESS_TOKEN, basicSupportApi.getAccessToken());
        params.put(ParamName.TYPE, "jsapi");
        String response = WxHttpUtils.doGet(url, params, wxInit.getCommonProp());
        JsonNode ticketNode = JacksonUtils.parse(response, JsonNode.class);
        JsApiTicket mTicket = null;
        if (ticketNode != null && ticketNode.get(ParamName.TICKET) != null) {
            String ticket = ticketNode.get(ParamName.TICKET).asText();
            mTicket = new JsApiTicket();
            mTicket.setTicket(ticket);
            mTicket.setLastDate(System.currentTimeMillis());
            mTicket.setAppId(wxInit.getCommonProp().getAppid());
            mTicket.setPublicId(wxInit.getCommonProp().getPublicId());
            mTicket.setInterval(ticketNode.get(ParamName.EXPIRES_IN).asInt() * 1000);
        }
        // 如果存在额外的缓存处理器
        String ticketCacheClass = "";
        if ("jsapi".equals(type)) {
            ticketCacheClass = wxInit.getSpiProp().getJsapiTicketCacheName();
        }
        if ("wx_card".equals(type)) {
            ticketCacheClass = wxInit.getSpiProp().getCardapiTicketCacheName();
        }
        if (StringUtils.hasText(ticketCacheClass)) {
            ApiDataCache<JsApiTicket> ticketCache = wxInit.getAppCtx().getBean(ticketCacheClass);
            if (ticketCache != null && mTicket != null) {
                ticketCache.save(mTicket);
                return;
            }
            return;
        }
        // 否则 缓存在本地
        if ("jsapi".equals(type)) {
            this.jsapiticket = mTicket;
        }
        if ("wx_card".equals(type)) {
            this.cardapiticket = mTicket;
        }
    }


    /**
     * 获取网页授权的用户信息
     *
     * @param code
     * @return
     * @throws Exception
     */
    public String getUser(String code) throws Exception {
        String webToken = getWebToken(code);
        if (!StringUtils.hasText(webToken)) {
            log.error("[网页授权获取用户信息] : 失败，token错误");
            return null;
        }
        JsonNode tokenNode = JacksonUtils.parse(webToken, JsonNode.class);
        String openid = tokenNode.get(ParamName.OPEN_ID).asText();
        // 这里使用基础获取用户信息，不用网页授权的形式，获取的信息没什么区别
        return userManage.getUser(openid);
    }

    /**
     * 获取网页授权的访问令牌
     *
     * @param code
     * @return
     */
    public String getWebToken(String code) {
        String url = wxInit.getApiProp().getWebAccessToken();
        Map<String, String> params = new HashMap<>();
        params.put(ParamName.APP_ID, wxInit.getCommonProp().getAppid());
        params.put(ParamName.SECRET, wxInit.getCommonProp().getAppSecret());
        params.put(ParamName.CODE, code);
        params.put(ParamName.GRANT_TYPE, "authorization_code");

        String response = WxHttpUtils.doGet(url, params, wxInit.getCommonProp());
        if (log.isDebugEnabled()) {
            log.debug("[获取网页登录凭证] : {}", response);
        }
        if (BasicSupportApi.isError(response)) {
            log.error("[网页授权凭证] : 失败 \r\n {}", response);
            return null;
        }
        return response;
    }

    public Map<String,Object> getJsSdkConfig(String url){
        try {
            url = URLDecoder.decode(url,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        if (url.indexOf("#") > 0) {
            url = url.substring(0,url.indexOf("#"));
        }
        String nonceStr = WxUtils.getDigitRandom(10);
        String timeStamp = System.currentTimeMillis() + "";
        String ticket = getCardapiTicket0();
        String encrypt = "jsapi_ticket=" + ticket
                       + "&noncestr=" + nonceStr + "&timestamp=" + timeStamp + "&url=" + url;
        String sign = SHA1Encrypt.SHA1(encrypt);

        Map<String,Object> result = new TreeMap<>();
        result.put("timestamp",timeStamp);
        result.put("nonceStr", nonceStr);
        result.put("appId",wxInit.getCommonProp().getAppid());
        result.put("signature",sign);
        return result;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (wxInit.getSpiProp().getLoadWebpageTicket() == 1) {
            resetApiTicket("jsapi");
            resetApiTicket("wx_card");
        }else{
            log.warn("[微信公众号网页授权Ticket]: 初始化：设置了禁止初始化。0：禁止；1：开启");
        }
    }
}
